﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace library
{

    public partial class Client : IDisposable
    {
        public delegate void SearchReturnHandler(string search, ValueHits<p2pPost> post);

        public static event SearchReturnHandler OnSearchReturn;


        public delegate void FileDownloadHandler(byte[] address, string filename);

        public static event FileDownloadHandler OnFileDownload;


        public delegate void FileUploadHandler(string filename, string base64Address);

        public static event FileUploadHandler OnFileUpload;

        static Cache<string> searching = new Cache<string>(10000);

        internal static Peer LocalPeer;

        internal static int P2pPort;

        internal static byte[] P2pAddress;

        public static IDisposable Start(byte[] p2pAddress, int p2pPort)
        {
            P2pAddress = p2pAddress;

            P2pPort = p2pPort;
            
            Network.Configure();

            LocalPeer = Peer.CreateLocalPeer();

            DelayedWrite.Start();

            p2pServer.Start();

            p2pRequest.Start();

            Peers.Start();

            LocalPackets.Start();

            LocalPosts.Load();

            p2pFile.Queue.Load();

            return new Client();
        }

        internal static bool Stop
        {
            get;
            private set;
        }

        public static void Close()
        {
            Stop = true;

            p2pServer.Stop();

            LocalPackets.Stop();

            Peers.Stop();

            p2pRequest.Stop();

            //LocalIndex.Save();

            LocalPosts.Save();

            DelayedWrite.Stop();
        }

        public static string GetWelcomeKey()
        {
            if (LocalPeer.EndPoint.Address.Equals(IPAddress.Any))
                return string.Empty;

            return Utils.ToBase64String(Addresses.ToBytes(LocalPeer.EndPoint));
        }

        public static bool AnyPeer()
        {
            return Peers.Any();
        }

        public static Dictionary<int, double> GetPeers()
        {
            Dictionary<int, double> result = new Dictionary<int, double>();

            Peer[] pp = Peers.GetPeers();

            foreach (Peer p in pp)
                result.Add(p.EndPoint.Port, p.Distance.Median);

            return result;
        }

        public static void GetPeer(string base64EndPoint)
        {
            byte[] b = Convert.FromBase64String(base64EndPoint.Replace('-', '/').Replace('(', '+')); //Utils.FromBase64String(base64EndPoint);

            IPEndPoint endpoint = new IPEndPoint(
                new IPAddress(b.Take(Parameters.ipv4Addresssize).ToArray()),
                BitConverter.ToUInt16(b, Parameters.ipv4Addresssize));

            Peer peer = Peers.GetPeerOrCreate(endpoint);

            p2pRequest.Send(targetPeer: peer, command: RequestCommand.Peer, returnPeer: peer);
        }

        public static void Search(string key)
        {
            lock (searching)
            {
                if (searching.Any(x => x.CachedValue.Equals(key)))
                    return;

                searching.Add(key);
            }

            var address = Utils.FromBase64String(key);

            var posts = new ValueHits<p2pPost>();

            if(address == null)
            {
                posts = LocalIndex.SearchPostsAddressesByTerm(key);
            }
            else
            {
                posts.AddRange(LocalPosts.Search(address));
            }

           

            SearchReturn(key, posts);

            //

            //if (address == null)
            //{
            //    ValueHits<byte[]> postsAddress = LocalIndex.SearchByTargetAddress(address);

            //    ValueHits posts = new ValueHits();

            //    postsAddress.ForEach(x => posts.Add(LocalPackets.Get(x.Value)));

            //    if (OnSearchReturn != null)
            //    {
            //        //posts.ForEach(x => 
            //        //SearchReturn(key,

            //        //    );


            //    }

            //}
            //else
            //{
            //    //Client.Get(requestParams, Utils.ToBase64Address(Utils.GetAddress()));
            //}
        }

        //public static void Search(string key)
        //{
        //    byte[] byte_key = Encoding.Unicode.GetBytes(key);

        //    ValueHits metadatas = LocalIndex.SearchMetadataAddressesByValue(byte_key);

        //    if (metadatas != null)
        //        foreach (ValueHitsItem item in metadatas)
        //        {
        //            byte[] data = LocalPackets.Get(item.Value);

        //            if (data == null)
        //                continue;

        //            Metadata metadata = Metadata.Load(new MemoryStream(data, 1, data.Length - 1), item.Value);

        //            SearchReturn(key,
        //                item.Hits + ": " +
        //                Encoding.Unicode.GetString(
        //                    metadata.Data.FirstOrDefault(
        //                        x => Addresses.Equals(x.Key, Encoding.Unicode.GetBytes("filename"))).Value) +
        //                ": " +
        //                Convert.ToBase64String(metadata.Address));
        //        }

        //    int requisitions = 0;

        //    while (requisitions < Parameters.propagation)
        //    {
        //        var sent = p2pRequest.Send(
        //                address: BitConverter.GetBytes(byte_key.Length).Concat(byte_key).ToArray(),
        //                command: RequestCommand.Metadata,
        //                ignoreDeadEnd: true,
        //                wait: false);

        //        if (sent)
        //            requisitions++;
        //        else
        //            break;
        //    }
        //}

        internal static void DownloadComplete(byte[] address, string filename)
        {
            if (OnFileDownload != null)
                OnFileDownload(address, filename);
        }


        static void SearchReturn(string search, ValueHits<p2pPost> post)
        {
            if (OnSearchReturn != null)
                OnSearchReturn(search, post);
        }

        public static byte[] Post(string post,string address = null, string target = null)
        {
            p2pPost p = p2pPost.Create(post, Utils.FromBase64String(address), Utils.FromBase64String(target));

            return p.Address;
        }

        public static void Upload(string filename)
        {
            Log.Write("[Upload] " + Utils.ToBase64String(p2pFile.Upload(filename)) + " " + filename);
        }

        public static byte[] Get(byte[] address)
        {
            var res = LocalPosts.Get(address);

            if(res == null)
                return null;

            return res.Data;
        }

        public static byte[] Get(string base64Address)
        {
            var address = Utils.FromBase64String(base64Address);

            if (address == null)
                return null;

            return Get(address);
        }

        public static void Download(string base64Address, string filename)
        {
            p2pFile.Queue.Add(base64Address, filename);
        }

        void IDisposable.Dispose()
        {
            Close();
        }
    }
}
